package com.snowy.hidden.interceptor;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSONObject;
import com.snowy.framework.common.base.vo.LoginUser;
import com.snowy.framework.common.resp.BaseResp;
import com.snowy.framework.common.resp.BaseRespUtil;
import com.snowy.framework.exception.UABPException;
import com.snowy.hidden.config.ConfigSetting;
import com.snowy.hidden.constant.AuthConstant;
import com.snowy.hidden.constant.AuthErrorCode;
import com.snowy.hidden.constant.RedisConstant;
import com.snowy.hidden.exception.SysErrorEnum;
import com.snowy.hidden.user.domain.model.SysUser;
import com.snowy.redis.RedisStaticUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * 登陆拦截器
 * 配置拦截器
 * @author hjl
 */
@Component
@Slf4j
public class AuthorHandle extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        if("GET".equals(request.getMethod())){
            log.info("==get请求忽略认证=="+request.getRequestURI());
            return true;
        }

        //判断是否忽略认证
        if (!authRequired(handlerMethod.getMethod())) {
            log.info("==忽略认证=="+request.getRequestURI());
            return true;
        }

        //接口权限合法性检查
//        BaseResp checkParam = this.validateReq(request);
//        if(!CommonConstant.RESP_SUCCESS_Status.equals(checkParam.getStatus())){
//            response.setStatus(Integer.parseInt(checkParam.getCode()));
//            throw new UABPException(checkParam.getCode(),checkParam.getMessage());
//        }

        //判断是否忽略登录
        if (!loginRequired(handlerMethod.getMethod())) {
            log.info("==忽略登录=="+request.getRequestURI());
            return true;
        }

        //判断token是否存在
        String token = request.getHeader("satoken");
        if (StringUtils.isBlank(token)) {
            response.setStatus(Integer.parseInt(AuthErrorCode.LOGIN_TIMEOUT_ERR.getReturnCode()));
            throw new UABPException(AuthErrorCode.LOGIN_TIMEOUT_ERR);
        }

        //判断是否登录
        LoginUser userInfoDto = this.getUserInfoByToken(token);
        if (null == userInfoDto) {
            response.setStatus(Integer.parseInt(AuthErrorCode.LOGIN_TIMEOUT_ERR.getReturnCode()));
            throw new UABPException(AuthErrorCode.LOGIN_TIMEOUT_ERR);
        }
        AuthConstant.userLocal.set(userInfoDto);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        super.afterCompletion(request, response, handler, ex);
    }

    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        super.afterConcurrentHandlingStarted(request, response, handler);
    }

    /**
     * 接口权限认证
     * @param request
     * @return
     */
    private BaseResp validateReq(HttpServletRequest request){
        /**
         * 获取权限参数
         */
        String timestamp = request.getHeader("timestamp");
        String sign = request.getHeader("sign");
        String appId = request.getHeader("appId");
        String nonceStr = request.getHeader("nonceStr");

        //测试环境忽略认证
        if(!"prod".equals(ConfigSetting.getProperty("environment.name"))){
            if("appid_lsfhm".equals(appId)){
                return BaseRespUtil.Success();
            }
        }

        /**
         * 权限参数校验
         */
        if(StrUtil.isBlank(timestamp) || StrUtil.isBlank(sign) || StrUtil.isBlank(appId) || StrUtil.isBlank(nonceStr)){
            return BaseRespUtil.Error(SysErrorEnum.ILLEGAL_PARAMETER);
        }

        //请求在2分钟之内有效
        long sysTime = System.currentTimeMillis();
        Long expireTime = 2 * 60 * 1000L;
        if (Math.abs(sysTime - Long.parseLong(timestamp)) > expireTime) {
            log.error(timestamp+"===="+appId+"==接口超时=="+request.getRequestURI()+"===="+sysTime);
            return BaseRespUtil.Error(SysErrorEnum.TIME_OUT);
        }

        //秘钥写死
        String sysAppId = "2022pipeline";
        String appSecret = "wxs2tfd4diu88df9";
        //校验请求是否合法
        String data = timestamp+sysAppId+nonceStr+appSecret;
        String localKey = SecureUtil.md5(data).toLowerCase();
        if (!localKey.equalsIgnoreCase(sign)) {
            return BaseRespUtil.Error(SysErrorEnum.ILLEGAL_PARAMETER);
        }
        return BaseRespUtil.Success();
    }

    /**
     * 判断一个方法是否忽略权限认证
     * @param method
     * @return
     */
    private boolean authRequired(Method method) {
        Annotation[] annotations = method.getAnnotations();
        // 判断是否有忽略认证的注解
        for (int i = 0, size = annotations.length; i < size; i++) {
            if (annotations[i] instanceof IgnoreAuth) {
                return false;
            }
        }
        return true;
    }

    /**
     * 判断一个方法是否需要登录
     * @param method
     * @return
     */
    private boolean loginRequired(Method method) {
        Annotation[] annotations = method.getAnnotations();
        // 判断是否有忽略登录的注解
        for (int i = 0, size = annotations.length; i < size; i++) {
            if (annotations[i] instanceof IgnoreLogin) {
                return false;
            }
        }
        return true;
    }

    /**
     * 根据token取用户信息
     * @param token
     * @return
     */
    public static LoginUser getUserInfoByToken(String token) {
        String userStr = RedisStaticUtil.getStr(RedisConstant.LOGIN_REDIS_USERINFO+token);
        LoginUser loginUser=null ;
        try {
            loginUser = JSONObject.parseObject(userStr, LoginUser.class);
        } catch (Exception e) {
            log.error("==getUserInfoByToken=="+e.getMessage());
        }
        return loginUser;
    }
}
